package org.example.neixing;

import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.*;

/**
 * 基于 java 内省机制实行的 bean 属性拷贝，一个简单的内省机制的使用，模拟成熟的 beanUtil 实现 bean 拷贝的原理
 */
public class BeanUtils {

    public static <T> Map<String, Object> beanToMap(T bean, boolean putIfNull)
            throws IntrospectionException, InvocationTargetException, IllegalAccessException {

        if (bean == null) {
            return new HashMap<>();
        }
        Map<String, Object> returnMap = new HashMap<>();

        // 获取bean的BeanInfo对象
        BeanInfo beanInfo = Introspector.getBeanInfo(bean.getClass(), Object.class);

        // 获取属性描述器
        PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();

        for (PropertyDescriptor propertyDescriptor : propertyDescriptors) {
            // 属性名
            String propertyName = propertyDescriptor.getName();
            // 获取该属性的值
            Method readMethod = propertyDescriptor.getReadMethod();
            // 属性的值
            Object value = readMethod.invoke(bean);
            if (value == null && !putIfNull) {
                continue;
            }
            returnMap.put(propertyName, value);
        }
        return returnMap;
    }

    public static <T> List<Map<String, Object>> beansToMaps(List<T> beans, boolean putIfNull)
            throws IllegalAccessException, IntrospectionException, InvocationTargetException {

        if (null == beans || beans.size() == 0) {
            return new ArrayList<>();
        }

        List<Map<String, Object>> result = new ArrayList<>(beans.size() + 1);
        // 转换每一个bean
        for (Object bean : beans) {
            result.add(beanToMap(bean, putIfNull));
        }
        return result;
    }

    public static <T> T mapToBean(Map<String, Object> map, Class<T> clz)
            throws IllegalAccessException, InstantiationException, IntrospectionException,
            InvocationTargetException {

        // 生成bean实例
        T bean = clz.newInstance();
        if (null == map) {
            return bean;
        }

        BeanInfo beanInfo = Introspector.getBeanInfo(clz, Object.class);
        PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();

        for (PropertyDescriptor propertyDescriptor : propertyDescriptors) {
            // 属性名
            String propertyName = propertyDescriptor.getName();
            // 获取属性值
            Object value = map.get(propertyName);
            // 写入属性值
            Method writeMethod = propertyDescriptor.getWriteMethod();
            writeMethod.invoke(bean, value);
        }
        return bean;
    }

    public static <T> List<T> mapsToBeans(List<Map<String, Object>> maps, Class<T> clz)
            throws InvocationTargetException, IntrospectionException, InstantiationException,
            IllegalAccessException {

        if (null == maps || maps.size() == 0) {
            return new ArrayList<>();
        }

        List<T> result = new ArrayList<>();
        for (Map<String, Object> map : maps) {
            result.add(mapToBean(map, clz));

        }
        return result;
    }

    public static <T1, T2> void copyProperties(T1 origin, T2 dest, boolean setNull,
                                               String[] excludeFieldNames)
            throws IntrospectionException, InvocationTargetException, IllegalAccessException {

        // 获取源类的BeanInfo对象
        BeanInfo originBeanInfo = Introspector.getBeanInfo(origin.getClass(), Object.class);
        // 获取源类的属性描述器
        PropertyDescriptor[] originPropertyDescriptors = originBeanInfo.getPropertyDescriptors();

        // 获取目标类的BeanInfo对象
        BeanInfo destBeanInfo = Introspector.getBeanInfo(dest.getClass(), Object.class);
        // 获取目标类的属性描述器
        PropertyDescriptor[] destPropertyDescriptors = destBeanInfo.getPropertyDescriptors();
        for (PropertyDescriptor propertyDescriptor : destPropertyDescriptors) {
            String propertyName = propertyDescriptor.getName();
            // 是否需要排除的属性
            boolean excludeField = false;
            if (excludeFieldNames != null) {
                for (String excludeFieldName : excludeFieldNames) {
                    if (Objects.equals(excludeFieldName, propertyName)) {
                        excludeField = true;
                        break;
                    }
                }
            }
            if (excludeField) {
                continue;
            }
            // 遍历源类的所有属性，如果存在此属性则进行拷贝
            for (PropertyDescriptor originPropertyDescriptor : originPropertyDescriptors) {
                String originPropertyName = originPropertyDescriptor.getName();
                if (Objects.equals(propertyName, originPropertyName)) {
                    // 读取属性值
                    Method readMethod = originPropertyDescriptor.getReadMethod();
                    Object srcValue = readMethod.invoke(origin);
                    if (srcValue != null || setNull) {
                        // 设置属性值
                        Method writeMethod = propertyDescriptor.getWriteMethod();
                        writeMethod.invoke(dest, srcValue);
                    }
                    break;
                }
            }
        }
    }

    public static <T1, T2> void copyProperties(T1 origin, T2 dest)
            throws IllegalAccessException, IntrospectionException, InvocationTargetException {
        copyProperties(origin, dest, false, null);
    }
}